在當今的網頁開發中,優化效能始終是一個追求的目標。我們都希望在短時間內,讓使用者得到最好的體驗。
於是,我們在開發過程中,努力地優化Lighthouse上的指標分數,其中透過code splitting以及lazy loading的方法,成功優化了網頁第一次渲染的速度,達到相當滿意的結果。然而,在遇到render-blocking,就是另外一個頭痛的問題。
當請求一個資源的時候,如果該資源存取完畢前無法觸發 window.onload 的事件,就稱作是 render-blocking。
透過深入分析,我們發現了一個不小的問題:載入Google Font字體。雖然這些精美的字體為我們的網頁增添了許多質感,但它在渲染過程中造成的堵塞卻是無法忽視的。畢竟,誰不想要一個既好看又快速的網站呢?
於是,我們開始著手解決這一問題。外部腳本的加載,我們都知道可以使用async或defer等方法優化,但Google Font的加載卻主要是透過<link>
載入,這使得優化變得有些許的困難。但困難不代表不可能,下面我會一步步地為大家解釋。
首先,我們先了解一下通常的Google Font載入方法:
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&
family=Noto+Sans+TC:wght@100;300;400;500;700&display=swap" rel="preload" as="style"/>
這樣的做法會導向一個CSS文件,該文件包含@font-face規則,而這些規則則由css下載字體檔,並套用。
hint:如果已經確定只需要某幾個字可以在網址後面輸入&text=xxx,google的cdn會自動只下載所需的字體檔。
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(https://fonts.gstatic.com/s/lato/v24/S6u9w4BMUTPHh7USSwiPGQ3q5d0.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
...
現在,讓我們來看如何優化這個過程。
以下是優化後的代碼:
<html lang="zh-Hant-TW">
...
<link rel="preconnect" href="https://fonts.googleapis.com" crossOrigin="anonymous" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&
family=Noto+Sans+TC:wght@100;300;400;500;700&display=swap" rel="preload" as="style"/>
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&family=Noto+Sans+TC:wght@100;300;400;500;700&display=swap"
media="print"
onload="this.media='all';"
/>
...
</html>
首先,我們可以使用preconnect來提前建立與字體伺服器的連接,減少等待時間。
接著,使用rel="preload"加速字體的載入,並使用media="print"和onload事件確保非同步載入,避免阻塞。
在這邊想補充:不同<link rel=xxx />
的意義:
rel:
preconnect
: 瀏覽器可以透過搶先發起到該來源的連線來改善使用者體驗,先執行TCP/IP解析域名的部分。preload
: 跟瀏覽器聲明取得的這個頁面很快需要的資源,您希望在頁面生命週期的早期、瀏覽器的主要渲染機制啟動之前開始載入這些資源。雖然安排以更高的優先權下載和快取腳本,但也不會載入和執行腳本。在還沒有下載完自定義字體前,display=swap
使用預設字體直到自定義字型加載完成後切換,然後利用media="print"
這個屬性,讓下載完的字體檔,以列印模式在為預設,不要直接渲染在不同頁面區塊造成帾塞。
最後在window.onload後再一次渲染上去。
hint:在這邊使用Next.js做專案,有遇到
<link>
這個Node不支援onload="this.media='all';"這個語法,所以我們利用了一點小技巧:直接套用html-string來繞過這個問題:
<style
dangerouslySetInnerHTML={{
__html: `</style><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Lato:wght@300;700&family=Noto+Sans+TC:wght@300;500;700&display=swap"
media="print"
onload="this.media='all';"
/>`,
}}
/>
藉由這種方法,我們成功地減少了渲染堵塞,提供了更好的使用者體驗。希望這篇文章對你們有所幫助,一起為更快速的網路世界努力吧!
參考資料:
[web] 內容 Image 預先或延遲載入(preload, lazy load, prefetch)
MDN:外部加載資源連結元素